package com.zzyy.study.aop;

import com.alibaba.fastjson2.JSON;
import com.zzyy.study.annotation.OperationLog;
import com.zzyy.study.config.AsyncFactory;
import com.zzyy.study.config.AsyncManager;
import com.zzyy.study.entities.OperationLogVo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @auther zzyy
 * @create 2024-02-17 16:40
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect
{
    /**
     * 设置操作日志切入点，记录操作日志，在注解(@OperationLogger)的位置切入代码
     * 容器捞鱼设计思想
     */
    @Around("@annotation(com.zzyy.study.annotation.OperationLog)")
    public Object around(ProceedingJoinPoint joinPoint)
    {
        Object result = null;
        OperationLogVo operationLogVo = null;

        //1 获得带着注解标签的方法名字,反射使用最重要的基本功
        String methodName = joinPoint.getTarget().getClass().getSimpleName()+"."+ joinPoint.getSignature().getName();
        
        try
        {
            //2 记录操作日志拼装OperationLogVo，监军太监开始工作
            operationLogVo = this.recordLog(joinPoint);
        }catch (Exception e){
            log.error("没有合适的web请求:{}",e.getMessage());
        }
        
        //3 记录下来新增addUser业务方法耗时/参数等其它记录细则
        long startTime = System.currentTimeMillis();
        try
        {
            result = joinPoint.proceed();//放行OK
            //正常返回的记录形成Json串保存
            operationLogVo.setData(JSON.toJSONString(result));
        }catch (Throwable e){
            log.info("method:{},throws:{}",methodName,ExceptionUtils.getStackTrace(e));
            if (operationLogVo != null)
            {
                operationLogVo.setErrormessage(stackTraceToString(e.getClass().getName(),e.getMessage(),e.getStackTrace()));
            }
        }finally {
            long endtTime = System.currentTimeMillis();
            if (operationLogVo != null) {
                operationLogVo.setTimes(endtTime - startTime);
                //4 异步多线程记录日志开启，重点精华内容
                AsyncManager.getInstance().execute(AsyncFactory.recordOperation(operationLogVo));
                //TODO 上述可以如何优化？？？ MQ或者大数据其它框架受理。
            }
        }
        return result;
    }

    private OperationLogVo recordLog(ProceedingJoinPoint joinPoint)
    {
        //1 反射技术的基本功，落地实现
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();

        //2 容器捞鱼，谁带着自定义注解@OperationLog("添加用户")
        OperationLog OperationLogAnnotation = method.getAnnotation(OperationLog.class);

        //3 web请求参数优化处理,为了获得HttpServletRequest
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        //4 日志保存进mysql或其它数据源
        OperationLogVo operationLogVo = new OperationLogVo();

        operationLogVo.setType(OperationLogAnnotation.value());
        operationLogVo.setRequestUri(request.getRequestURI());
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = method.getName();
        methodName = className + "." + methodName;
        // 请求方法
        operationLogVo.setName(methodName);

        // 请求参数
        if (joinPoint.getArgs()[0] instanceof MultipartFile) {
            operationLogVo.setParams(((MultipartFile) joinPoint.getArgs()[0]).getOriginalFilename());
        } else {
            operationLogVo.setParams(JSON.toJSONString(joinPoint.getArgs()));//DTO
        }
        // 请求方式
        operationLogVo.setMethod(Objects.requireNonNull(request).getMethod());
        // 请求用户ID 先写死
        operationLogVo.setUserid("zzyy");
        // 请求用户昵称 先写死
        operationLogVo.setNickName("zzyyNickName");
        // 操作ip地址
        String ip = request.getRemoteAddr();
        operationLogVo.setIpAddress(ip);

        return operationLogVo;
    }

    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement stet : elements) {
            stringBuilder.append(stet).append("\n");
        }
        return exceptionName + ":" + exceptionMessage + "\n" + stringBuilder;
    }

}



